package com.rjj.wlqq.tool.zip;

import com.sun.istack.internal.NotNull;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
public class ZipUtils {

    private ZipUtils() {
    }

    /**
     * 多个文件下载为zip文件
     *
     * @param fileList       文件集合
     * @param response       响应
     * @param outFileZipName 输出文件名称
     */
    public static void doCompress(List<File> fileList, HttpServletResponse response, String outFileZipName) throws IOException {
        ServletOutputStream outputStream = response.getOutputStream();
        try {
            response.setContentType("APPLICATION/OCTET-STREAM");
            response.setHeader("Content-Disposition", "attachment; filename=" + outFileZipName);
            for (File file : fileList) {
                ZipUtils.doZip(file, new ZipOutputStream(outputStream), "");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            outputStream.close();
        }

    }

    public static void doCompress(String srcFile, String zipFile) throws IOException {
        doCompress(new File(srcFile), new File(zipFile));
    }

    /**
     * 文件压缩
     *
     * @param srcFile 目录或者单个文件
     * @param zipFile 压缩后的ZIP文件
     */
    public static void doCompress(File srcFile, File zipFile) throws IOException {
        try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile))) {
            doCompress(srcFile, out);
        }
    }

    /**
     * @param filelName      单个文件或者目录
     * @param outFileZipName 输出文件名称
     */
    public static ZipOutputStream doCompress(String filelName, String outFileZipName, @NotNull HttpServletResponse response) throws IOException {
        response.setContentType("APPLICATION/OCTET-STREAM");
        response.setHeader("Content-Disposition", "attachment; filename=" + outFileZipName);
        ZipOutputStream out = new ZipOutputStream(response.getOutputStream());
        doCompress(filelName, out);
        response.flushBuffer();
        return out;
    }

    public static void doCompress(String filelName, ZipOutputStream out) throws IOException {
        doCompress(new File(filelName), out);
    }

    public static void doCompress(File file, ZipOutputStream out) throws IOException {
        doCompress(file, out, "");
    }

    /**
     * @param inFile 输入的文件路径（或者文件）
     * @param out    zip输出流
     * @param dir    压缩文件中在加一个路径
     *               <p>
     *               迭代：循环中有多少个循环就会执行多少个循环，每个循环有每个循环的任务，一个一个执行
     */
    public static void doCompress(File inFile, ZipOutputStream out, String dir) throws IOException {
        if (inFile.isDirectory()) {
            //获取路径下的所有文件名称
            File[] files = inFile.listFiles();
            if (files != null && files.length > 0) {
                for (File file : files) {
                    String name = inFile.getName();
                    if (!"".equals(dir)) {
                        name = dir + "/" + name;
                    }
                    //是路径这里迭代继续查找文件
                    ZipUtils.doCompress(file, out, name);
                }
            }
        } else {
            //不是路径的时候直接压缩
            ZipUtils.doZip(inFile, out, dir);
        }
    }

    /**
     * 最慢的
     *
     * @param inFile 输入文件
     * @param out    输出流
     * @param dir    压缩文件中的文件路径名称
     * @throws IOException
     */
    private static void doZip(File inFile, ZipOutputStream out, String dir) throws IOException {
        String entryName = null;
        if (!"".equals(dir)) {
            entryName = dir + "/" + inFile.getName();
        } else {
            entryName = inFile.getName();
        }
        ZipEntry entry = new ZipEntry(entryName);
        out.putNextEntry(entry);

        int len = 0;
        byte[] buffer = new byte[1024];
        FileInputStream fis = new FileInputStream(inFile);
        while ((len = fis.read(buffer)) > 0) {
            out.write(buffer, 0, len);
            out.flush();
        }
        out.closeEntry();
        fis.close();
    }

    /**
     * 这里直接是多文件压缩
     * 最快
     * 通过开通道的方式速度会更加快
     *
     * @param inFile          要压缩的文件【可以是文件，也可是文件路径】
     * @param zipOutputStream 输出流
     * @param dir             压缩文件都前一个文件夹
     * @throws IOException 异常
     */
    private static void doFiles2Zip(File inFile, ZipOutputStream zipOutputStream, String dir) throws IOException {
        log.info("开始压缩文件。。");
        //获取所有的文件
        if (inFile.isDirectory()) {
            //获取路径下的所有文件名称
            File[] files = inFile.listFiles();
            if (files != null && files.length > 0) {
                for (File file : files) {
                    //是路径这里迭代继续查找文件
                    ZipUtils.doFiles2Zip(file, zipOutputStream, dir);
                }
            } else {
                //空文件夹
                String absolutePath = inFile.getAbsolutePath();
                System.out.println(absolutePath);
                zipOutputStream.putNextEntry(new ZipEntry(absolutePath + "/"));
                zipOutputStream.closeEntry();
            }
        } else {

            //重复文件名称问题，这个就把全部文件夹都压缩
            //String entryName = inFile.getName();
            String entryName = inFile.getAbsolutePath();
            if (!"".equals(dir)) {
                entryName = dir + "/" + entryName;
            }
            zipOutputStream.putNextEntry(new ZipEntry(entryName));
            FileChannel channel = new FileInputStream(inFile).getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            while (channel.read(byteBuffer) != -1) {
                byteBuffer.clear();
                zipOutputStream.write(byteBuffer.array());
            }
            channel.close();
            zipOutputStream.closeEntry();
        }
        log.info("压缩文件执行完毕。。");
    }

    /**
     * 缓存方式第二快
     * @param inFile
     * @param zipOutputStream
     * @param dir
     * @throws IOException
     */
    private static void doFiles2Zip2(File inFile, ZipOutputStream zipOutputStream, String dir) throws IOException {
        log.info("开始压缩文件。。");
        //获取所有的文件
        if (inFile.isDirectory()) {
            //获取路径下的所有文件名称
            File[] files = inFile.listFiles();
            if (files != null && files.length > 0) {
                for (File file : files) {
                    //是路径这里迭代继续查找文件
                    ZipUtils.doFiles2Zip(file, zipOutputStream, dir);
                }
            } else {
                //空文件夹
                String absolutePath = inFile.getAbsolutePath();
                System.out.println(absolutePath);
                zipOutputStream.putNextEntry(new ZipEntry(absolutePath + "/"));
                zipOutputStream.closeEntry();
            }
        } else {

            //重复文件名称问题，这个就把全部文件夹都压缩
            //String entryName = inFile.getName();
            String entryName = inFile.getAbsolutePath();
            if (!"".equals(dir)) {
                entryName = dir + "/" + entryName;
            }
            try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(zipOutputStream)) {
                //获取输入文件缓存
                try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(inFile))) {
                    zipOutputStream.putNextEntry(new ZipEntry(entryName));
                    int temp = 0;
                    while (-1 != (temp = bufferedInputStream.read())) {
                        bufferedOutputStream.write(temp);
                        bufferedOutputStream.flush();
                    }
                }
                zipOutputStream.closeEntry();
            }
        }
        log.info("压缩文件执行完毕。。");
    }

    public static void main(String[] args) throws IOException {
        //doCompress("/Users/renjianjun/Downloads/斗图/", "/Users/renjianjun/Downloads/a.zip");
        //doCompress(new File("/Users/renjianjun/Downloads/我的/斗图/"),new ZipOutputStream(new FileOutputStream(new File("/Users/renjianjun/study/ideaWorkSpace/wlqq/target/a.zip"))),"rjj");
        //doFiles2Zip(new File("/Users/renjianjun/Downloads/我的/斗图/"), new ZipOutputStream(new FileOutputStream(new File("/Users/renjianjun/study/ideaWorkSpace/wlqq/target/a.zip"))), "rjj");
        doFiles2Zip2(new File("/Users/renjianjun/Downloads/我的/斗图/"), new ZipOutputStream(new FileOutputStream(new File("/Users/renjianjun/study/ideaWorkSpace/wlqq/target/a.zip"))), "rjj");
    }

}